﻿// 版权所有：
// 文 件  名：Xml.cs
// 功能描述：XML文件操作类
// 创建标识：Seven Song(m.sh.lin0328@163.com) 2013/1/26 12:26:50
// 修改描述：
//----------------------------------------------------------------*/
using System;
using System.Collections.Generic;

using System.Text;
using System.Xml;
using System.Data;

namespace MSL.Utility.Doc
{
    /// <summary>
    /// xml操作类<br/>
    /// 2013-1-5 马山林 添加<br/>
    /// 修改记录：2013-1-6 添加读取xml文本功能<br/>
    /// 修改记录：2014-3-26 添加常用方法 规范代码并测试功能<br/>
    /// </summary>
    public class Xml
    {
        #region 变量
        /// <summary>
        /// 
        /// </summary>
        private XmlDocument _xmlDoc;
        /// <summary>
        /// XML文件的物理路径
        /// </summary>
        private string _path = string.Empty;
        /// <summary>
        /// XML的根节点
        /// </summary>
        private XmlElement _element;

        private const string CDATALEFT = "<![CDATA[";
        private const string CDATARIGHT = "]]>";
        #endregion

        #region 构造函数

        /// <summary>
        /// 构造函数，导入Xml文件
        /// </summary>
        public Xml()
        {
            _xmlDoc = new XmlDocument();
        }

        /// <summary>
        /// 构造函数，导入Xml文件
        /// </summary>
        /// <param name="xmlFile">文件路径或xml文件文本</param>
        public Xml(string xmlFile)
        {
            try
            {
                _xmlDoc = new XmlDocument();
                if (Utility.Text.CString.OccurNum("version", xmlFile.Length > 50 ? xmlFile.Substring(0, 50) : xmlFile) > 0)
                {
                    _xmlDoc.LoadXml(xmlFile);
                }
                else
                {
                    this._path = xmlFile;
                    _xmlDoc.Load(xmlFile);   //载入Xml文档
                }
                //为XML的根节点赋值
                _element = _xmlDoc.DocumentElement;
            }
            catch (System.Exception ex)
            {
                throw ex;
            }
        }
        /// <summary>
        /// 析构函数
        /// </summary>
        ~Xml()
        {

            _xmlDoc = null;  //释放XmlDocument对象
        }

        #endregion

        #region 创建一个xml文件
        /// <summary>
        /// 创建一个XML文档
        /// </summary>
        /// <param name="xmlFileName">XML文档含物理路径</param>
        /// <param name="rootNodeName">XML文档根节点名称(须指定一个根节点名称)</param>
        /// <param name="version">XML文档版本号(必须为:"1.0")</param>
        /// <param name="encoding">XML文档编码方式</param>
        /// <param name="standalone">该值必须是"yes"或"no",如果为null,Save方法不在XML声明上写出独立属性</param>
        /// <returns>成功返回true,失败返回false</returns>
        public bool CreateXmlDocument(string xmlFileName, string rootNodeName, string version, string encoding, string standalone)
        {
            bool isSuccess = false;

            try
            {
                XmlDeclaration xmlDeclaration = _xmlDoc.CreateXmlDeclaration(version, encoding, standalone);
                XmlNode root = _xmlDoc.CreateElement(rootNodeName);
                _xmlDoc.AppendChild(xmlDeclaration);
                _xmlDoc.AppendChild(root);
                _xmlDoc.Save(xmlFileName);
                isSuccess = true;
                _element = _xmlDoc.DocumentElement;
            }
            catch (Exception ex)
            {
                throw ex; //这里可以定义你自己的异常处理
            }
            return isSuccess;
        }
        #endregion

        #region 查询
        /// <summary>
        /// 查询指定节点的节点值 马山林 2014-01-17
        /// </summary>
        /// <param name="xPath">节点路径</param>
        /// <returns></returns>
        public List<Dictionary<String, String>> GetNodeValues(string xPath)
        {
            return GetNodeValues(xPath, null);
        }
        /// <summary>
        /// 查询指定节点的属性值与节点值 马山林 2014-01-17
        /// </summary>
        /// <param name="xPath">节点路径</param>
        /// <param name="attributeArr">属性数组，可以为空</param>
        /// <returns></returns>
        public List<Dictionary<String, String>> GetNodeValues(string xPath, string[] attributeArr)
        {
            List<Dictionary<String, String>> list = null;
            if (String.IsNullOrEmpty(xPath)) return list;
            if (xPath.EndsWith("/")) xPath = xPath.Substring(0, xPath.Length - 1);

            XmlNodeList nodeList = _element.SelectNodes(xPath);
            XmlNode xmlNode = null;
            if (nodeList.Count > 0)
            {
                list = new List<Dictionary<string, string>>();
                for (int i = 0; i < nodeList.Count; i++)
                {
                    Dictionary<String, String> dic = new Dictionary<string, string>();
                    xmlNode = nodeList.Item(i);
                    if (attributeArr != null && attributeArr.Length > 0)
                    {
                        foreach (string item in attributeArr)
                        {
                            try
                            {
                                dic[item] = xmlNode.Attributes[item].Value.Trim();
                            }
                            catch (Exception)
                            {

                            }
                        }
                    }
                    dic["value"] = xmlNode.InnerText.Replace("\r\n", "").Trim();//接点值
                    list.Add(dic);
                }
            }
            return list;
        }
        /// <summary>
        /// 查询指定节点的指定属性值
        /// </summary>
        /// <param name="xPath">节点名</param>
        /// <param name="attributeName">属性名</param>
        /// <returns>属性值</returns>
        public string GetNodeValue(string xPath, string attributeName)
        {
            string strReturn = "";

            try
            {
                //根据指定路径获取节点
                XmlNode xmlNode = _element.SelectSingleNode(xPath);
                //获取节点的属性，并循环取出需要的属性值
                XmlAttributeCollection xmlAttr = xmlNode.Attributes;

                for (int i = 0; i < xmlAttr.Count; i++)
                {
                    if (xmlAttr.Item(i).Name == attributeName)
                        strReturn = xmlAttr.Item(i).Value;
                }
            }
            catch (XmlException xmle)
            {
                throw xmle;
            }
            return strReturn;
        }
        /// <summary>
        /// 查询节点列表
        /// </summary>
        /// <param name="xPath">节点名</param>
        /// <param name="attributeName">属性名 可以为空</param>
        /// <param name="attributeValue">属性值</param>
        /// <returns>属性值</returns>
        public List<XmlNode> GetNodeList(string xPath, string attributeName, string attributeValue)
        {
            List<XmlNode> nodeList = new List<XmlNode>();

            try
            {
                //根据指定路径获取节点
                foreach (XmlNode node in _element.SelectNodes(xPath))
                {
                    if (String.IsNullOrEmpty(attributeName))
                    {
                        nodeList.Add(node);
                    }
                    else
                    {
                        foreach (XmlAttribute item in node.Attributes)
                        {
                            if (item.Name == attributeName && item.Value == attributeValue)
                            {
                                nodeList.Add(node);
                                break;
                            }
                        }
                    }
                }
            }
            catch (XmlException xmle)
            {
                throw xmle;
            }
            return nodeList;
        }

        /// <summary>
        /// 根据Xml文件的节点路径，返回一个DataSet数据集
        /// </summary>
        /// <param name="xmlPathNode">Xml文件的某个节点</param>
        /// <returns></returns>
        public DataTable GetDt(string xmlPathNode)
        {

            DataSet ds = new DataSet();
            try
            {
                System.IO.StringReader read = new System.IO.StringReader(_element.SelectSingleNode(xmlPathNode).OuterXml);
                ds.ReadXml(read);   //利用DataSet的ReadXml方法读取StringReader文件流
                read.Close();
            }
            catch
            { }
            if (ds.Tables.Count > 0)
            {
                return ds.Tables[0];
            }
            else
            {
                DataTable dt = new DataTable();
                return dt;
            }
        }

        #region 获取指定XPath表达式节点的属性值
        /// <summary>
        /// 获取指定XPath表达式节点的属性值
        /// </summary>
        /// <param name="xPath">XPath表达式,
        /// 范例1: @"Skill/First/SkillItem", 等效于 @"//Skill/First/SkillItem"
        /// 范例2: @"Table[USERNAME='a']" , []表示筛选,USERNAME是Table下的一个子节点.
        /// </param>
        /// <param name="attributeName">属性名</param>
        public string GetAttribute(string xPath, string attributeName)
        {
            string attributeValue = String.Empty;
            //返回XPath节点的属性值
            try
            {
                attributeValue = _element.SelectSingleNode(xPath).Attributes[attributeName].Value;
            }
            catch (Exception)
            {

            }
            return attributeValue;
        }
        #endregion

        #region 获取指定XPath表达式节点的值
        /// <summary>
        /// 获取指定XPath表达式节点的值
        /// </summary>
        /// <param name="xPath">XPath表达式,
        /// 范例1: @"Skill/First/SkillItem", 等效于 @"//Skill/First/SkillItem"
        /// 范例2: @"Table[USERNAME='a']" , []表示筛选,USERNAME是Table下的一个子节点.
        /// 范例3: @"ApplyPost/Item[@itemName='岗位编号']",@itemName是Item节点的属性.
        /// </param>
        public string GetNodeValue(string xPath)
        {
            //返回XPath节点的值
            string _nodeTxt = _element.SelectSingleNode(xPath).InnerText;
            if (_nodeTxt == null || _nodeTxt == "")
                return "";
            else
                return _nodeTxt;
        }

        #endregion

        /// <summary>
        /// 节点值查询判断
        /// </summary>
        /// <param name="xPath">节点路径</param>
        /// <param name="index">节点索引</param>
        /// <param name="nodeText">节点值</param>
        /// <returns></returns>
        public bool SelectNode(string xPath, int index, string nodeText)
        {
            try
            {
                XmlNodeList nodeList = _element.SelectNodes(xPath);
                //循环遍历节点，查询是否存在该节点
                for (int i = 0; i < nodeList.Count; i++)
                {
                    if (nodeList[i].ChildNodes[index].InnerText == nodeText)
                    {
                        return true;
                    }
                }
            }
            catch
            {
                return false;
            }
            return false;
        }

        /// <summary>
        /// 是否包含节点
        /// </summary>
        /// <param name="xPath">节点路径</param>
        /// <returns></returns>
        public bool Contains(string xPath)
        {
            return _element.SelectNodes(xPath).Count > 0 ? true : false;
        }

        /// <summary>
        /// 指定XPath表达式节点是否包含属性
        /// </summary>
        /// <param name="xPath">XPath表达式,
        /// 范例1: @"Skill/First/SkillItem", 等效于 @"//Skill/First/SkillItem"
        /// 范例2: @"Table[USERNAME='a']" , []表示筛选,USERNAME是Table下的一个子节点.
        /// </param>
        /// <param name="attributeName">属性名</param>
        public bool HasAttribute(string xPath, string attributeName)
        {
            bool result = false;
            try
            {
                XmlAttributeCollection attrs = _element.SelectSingleNode(xPath).Attributes;
                foreach (XmlAttribute xa in attrs)
                {
                    if (xa.Name.Equals(attributeName))
                    {
                        result = true;
                        break;
                    }
                }
            }
            catch (Exception)
            {

            }
            return result;
        }

        #region 获取指定XPath表达式的节点对象
        /// <summary>
        /// 获取指定XPath表达式的节点对象
        /// </summary>        
        /// <param name="xPath">XPath表达式,
        /// 范例1: @"Skill/First/SkillItem", 等效于 @"//Skill/First/SkillItem"
        /// 范例2: @"Table[USERNAME='a']" , []表示筛选,USERNAME是Table下的一个子节点.
        /// 范例3: @"ApplyPost/Item[@itemName='岗位编号']",@itemName是Item节点的属性.
        /// </param>
        public XmlNode GetNode(string xPath)
        {
            //返回XPath节点
            return _element.SelectSingleNode(xPath);
        }
        #endregion

        /// <summary>
        /// 获取子节点个数
        /// </summary>
        /// <param name="xPath">节点路径</param>
        /// <returns></returns>
        public Int32 GetSubNodesCount(string xPath)
        {
            int i = 0;
            try
            {
                i = _element.SelectSingleNode(xPath).ChildNodes.Count;
            }
            catch
            {
                i = 0;
            }
            return i;
        }

        /// <summary>
        /// 根据id获得XmlElement
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public XmlElement GetElementById(string id)
        {
            return _xmlDoc.GetElementById(id);
        }
        /// <summary>
        /// 根据id获得节点值
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public string GetElementInnerTextById(string id)
        {
            XmlElement ele = _xmlDoc.GetElementById(id);
            if (ele != null)
                return _xmlDoc.GetElementById(id).InnerText;
            return "";
        }
        /// <summary>
        /// 根据节点名获得子节点列表
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public XmlNodeList GetNodesByTagName(string name)
        {
            return _xmlDoc.GetElementsByTagName(name);
        }

        /// <summary>
        /// 根据节点名获得第一个子节点
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public XmlNode GetNodeByTagName(string name)
        {
            XmlNodeList nl = _xmlDoc.GetElementsByTagName(name);
            if (nl != null && nl.Count > 0)
                return nl[0];
            return null;
        }

        /// <summary>
        /// 根据节点名获得第一个子节点的值
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public string GetElementByTagNameString(string name)
        {
            XmlNodeList nl = _xmlDoc.GetElementsByTagName(name);
            if (nl != null && nl.Count > 0)
                return nl[0].InnerText;
            return null;
        }

        #endregion

        #region 改
        /// <summary>
        /// 设置节点值
        /// </summary>
        /// <param name="xPath">节点名</param>
        /// <param name="xmlNodeValue">节点值</param>
        public void SetNode(string xPath, string xmlNodeValue)
        {
            try
            {
                //根据指定路径获取节点
                XmlNode xmlNode = _element.SelectSingleNode(xPath);
                //设置节点值
                xmlNode.InnerText = xmlNodeValue;
            }
            catch (XmlException xmle)
            {
                throw xmle;
            }
        }

        /// <summary>
        /// 设置节点属性值 
        /// </summary>
        /// <param name="xPath">节点名</param>
        /// <param name="attributeName">属性名</param>
        /// <param name="attributeValue">属性值</param>
        public void SetAttribute(string xPath, string attributeName, string attributeValue)
        {
            try
            {
                //根据指定路径获取节点
                XmlNode xmlNode = _element.SelectSingleNode(xPath);
                xmlNode.Attributes[attributeName].Value = attributeValue;
            }
            catch (XmlException xmle)
            {
                throw xmle;
            }
        }

        /// <summary>
        /// 更新N个节点值
        /// </summary>
        /// <param name="xPath">父节点</param>
        /// <param name="xmlNode">子节点</param>
        /// <param name="nodeContent">子节点内容</param>
        /// <returns></returns>
        public bool SetNodes(string xPath, string[] xmlNode, string[] nodeContent)
        {

            try
            {
                //根据节点数组循环修改节点值
                for (int i = 0; i < xmlNode.Length; i++)
                {
                    _element.SelectSingleNode(xPath + "/" + xmlNode[i]).InnerText = nodeContent[i];
                }
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 根据id设置节点值
        /// </summary>
        /// <param name="id"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public void SetElementInnerTextById(string id, string value)
        {
            XmlElement ele = _xmlDoc.GetElementById(id);
            if (ele != null)
                _xmlDoc.GetElementById(id).InnerText = value;
        }
        #endregion

        #region 增
        /// <summary>
        /// 插入一个节点，带N个子节点
        /// </summary>
        /// <param name="xPath">插入节点的父节点</param>
        /// <param name="childNode">插入节点的元素名</param>
        /// <param name="elements">插入节点的子节点名数组</param>
        /// <param name="contents">插入节点的子节点内容数组</param>
        /// <returns></returns>
        public bool AppendNode(string xPath, string childNode, string[] elements, string[] contents)
        {
            try
            {
                XmlNode objRootNode = _element.SelectSingleNode(xPath);    //声明XmlNode对象
                XmlElement objChildNode = _xmlDoc.CreateElement(childNode);  //创建XmlElement对象
                objRootNode.AppendChild(objChildNode);
                for (int i = 0; i < elements.Length; i++)    //循环插入节点元素
                {
                    XmlElement objElement = _xmlDoc.CreateElement(elements[i]);
                    objElement.InnerText = contents[i];
                    objChildNode.AppendChild(objElement);
                }
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 插入数据
        /// </summary>
        /// <param name="xPath">节点</param>
        /// <param name="element">元素名，非空时插入新元素，否则在该元素中插入属性</param>
        /// <param name="attribute">属性名，非空时插入该元素属性值，否则插入元素值</param>
        /// <param name="value">值</param>
        /// <returns></returns>
        /**************************************************
         * 使用示列:
         * XmlHelper.Insert(path, "/Node", "Element", "", "Value")
         * XmlHelper.Insert(path, "/Node", "Element", "Attribute", "Value")
         * XmlHelper.Insert(path, "/Node", "", "Attribute", "Value")
         ************************************************/
        public void AppendNode(string xPath, string element, string attribute, string value)
        {
            try
            {
                XmlNode xn = _element.SelectSingleNode(xPath);
                if (element.Equals(""))
                {
                    if (!attribute.Equals(""))
                    {
                        XmlElement xe = (XmlElement)xn;
                        xe.SetAttribute(attribute, value);
                    }
                }
                else
                {
                    XmlElement xe = _xmlDoc.CreateElement(element);
                    if (attribute.Equals(""))
                        xe.InnerText = value;
                    else
                        xe.SetAttribute(attribute, value);
                    xn.AppendChild(xe);
                }
                _xmlDoc.Save(this._path);
                //_xmlDoc.Save(path);
            }
            catch { }
        }

        /// <summary>
        /// 添加属性
        /// </summary>
        /// <param name="xPath">属性所在节点</param>
        /// <param name="attribute">属性名</param>
        /// <param name="attributeValue">属性值</param>
        /// <returns></returns>
        public bool AddAttribute(string xPath, string attribute, string attributeValue)
        {
            try
            {
                XmlElement xe = (XmlElement)_element.SelectSingleNode(xPath); //强制转化成XmlElement对象
                xe.SetAttribute(attribute, attributeValue);    //XmlElement对象添加属性方法     
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 1. 功能：新增节点。
        /// 2. 使用条件：将任意节点插入到当前Xml文件中。
        /// </summary>        
        /// <param name="xmlNode">要插入的Xml节点</param>
        public void AppendNode(XmlNode xmlNode)
        {
            //导入节点
            XmlNode node = _xmlDoc.ImportNode(xmlNode, true);

            //将节点插入到根节点下
            _element.AppendChild(node);
        }

        /// <summary>
        /// 1. 功能：新增节点。
        /// 2. 使用条件：将DataSet中的第一条记录插入Xml文件中。
        /// </summary>        
        /// <param name="ds">DataSet的实例，该DataSet中应该只有一条记录</param>
        public void AppendNode(DataSet ds)
        {
            //创建XmlDataDocument对象
            XmlDataDocument xmlDataDocument = new XmlDataDocument(ds);

            //导入节点
            XmlNode node = xmlDataDocument.DocumentElement.FirstChild;

            //将节点插入到根节点下
            AppendNode(node);
        }

        #endregion

        #region 删除

        /// <summary>
        /// 删除数据
        /// </summary>
        /// <param name="xPath">路径</param>
        /// <param name="attribute">值</param>
        /// <returns></returns>
        public void RemoveAttribute(string xPath, string attribute)
        {
            try
            {
                XmlNode xn = _element.SelectSingleNode(xPath);
                XmlElement xe = (XmlElement)xn;
                if (!String.IsNullOrEmpty(attribute))
                    xe.RemoveAttribute(attribute);

                //_xmlDoc.Save(xPath);
            }
            catch { }
        }

        /// <summary>
        /// 删除指定XPath表达式的节点
        /// </summary>        
        /// <param name="xPath">XPath表达式,
        /// 范例1: @"Skill/First/SkillItem", 等效于 @"//Skill/First/SkillItem"
        /// 范例2: @"Table[USERNAME='a']" , []表示筛选,USERNAME是Table下的一个子节点.
        /// 范例3: @"ApplyPost/Item[@itemName='岗位编号']",@itemName是Item节点的属性.
        /// </param>
        public void RemoveNodes(string xPath)
        {
            RemoveNodes(xPath, null, null);
        }
        /// <summary>
        /// 删除指定XPath表达式的节点
        /// </summary>        
        /// <param name="xPath">XPath表达式,
        /// 范例1: @"Skill/First/SkillItem", 等效于 @"//Skill/First/SkillItem"
        /// 范例2: @"Table[USERNAME='a']" , []表示筛选,USERNAME是Table下的一个子节点.
        /// 范例3: @"ApplyPost/Item[@itemName='岗位编号']",@itemName是Item节点的属性.
        /// <param name="attributeName">属性名 可以为空</param>
        /// <param name="attributeValue">属性值</param>
        /// </param>
        public void RemoveNodes(string xPath, string attributeName, string attributeValue)
        {
            //根据指定路径获取节点
            foreach (XmlNode node in _element.SelectNodes(xPath))
            {
                if (String.IsNullOrEmpty(attributeName))
                {
                    _element.RemoveChild(node);
                }
                else
                {
                    foreach (XmlAttribute item in node.Attributes)
                    {
                        if (item.Name == attributeName)
                        {
                            if ((!String.IsNullOrEmpty(attributeValue) && item.Value == attributeValue) || String.IsNullOrEmpty(attributeValue))
                            {
                                _element.RemoveChild(node);
                                break;
                            }
                        }
                    }
                }
            }
        }
        #endregion

        #region CDATA

        /// <summary>
        /// 给指定的字符加上CDATA
        /// </summary>
        /// <param name="text">对象字符</param>
        /// <returns></returns>
        public string SetCDATA(string text)
        {
            if (string.IsNullOrEmpty(text))
            {
                return text;
            }
            text = text.Replace(CDATALEFT, string.Empty).Replace(CDATARIGHT, string.Empty);
            text = text.Replace("\\", "#!!#").Replace("\0", "#!0!#");
            return CDATALEFT + text + CDATARIGHT;
        }
        /// <summary>
        /// 清除CDATA
        /// </summary>
        /// <param name="text">对象字符</param>
        /// <returns></returns>
        public string ClearCDATA(string text)
        {
            if (string.IsNullOrEmpty(text))
            {
                return text;
            }
            text = text.Replace("#!!#", "\\").Replace("#!0!#", "\\0");
            text = text.Replace(CDATALEFT, string.Empty).Replace(CDATARIGHT, string.Empty);
            return text;
        }
        #endregion

        #region 保存XML文件
        /// <summary>
        /// 保存XML文件
        /// </summary>        
        public void Save()
        {
            //保存XML文件
            _xmlDoc.Save(this._path);
        }
        #endregion //保存XML文件

        #region 创建根节点对象
        /// <summary>
        /// 创建根节点对象
        /// </summary>
        /// <param name="xmlFilePath">Xml文件的绝对路径</param>        
        private static XmlElement CreateRootElement(string xmlFilePath)
        {
            //创建XmlDocument对象
            XmlDocument xmlDocument = new XmlDocument();
            //加载XML文件
            xmlDocument.Load(xmlFilePath);

            //返回根节点
            return xmlDocument.DocumentElement;
        }
        #endregion
    }
}
